#ifndef __tenacitas_log__file__h__
#define __tenacitas_log__file__h__


// =============================================================================
/** @file In this file are implemented the classes: tenacitas::log::file,
 tenacitas::log::default_change_file_policy, tenacitas::log::default_text_line
 and the typedef tenacitas::log::default_text_file */

// =============================================================================
/** todo
    @todo implement class @p default_binary_line
*/

// =============================================================================
//                                 C++ Headers
#include <string>
#include <cstdint>
#include <sstream>
#include <fstream>
#include <memory>
#include <chrono>
#include <cstdio>
#include <utility>
#include <type_traits>

// =============================================================================
//                                 3rds Headers
#include <boost/thread/thread.hpp>

// =============================================================================
//                                 Our Headers
#include "level.h"

// =============================================================================
//                                 Namespaces
using namespace std;


// =============================================================================
//                                 Macro Contants
/** */


// =============================================================================
//                                 Macro Commands
/** */


/** namespace of the organization */
namespace tenacitas {
    namespace log {

        // =========================== Typedefs ===========================
        /** */

        // ============================ Pre-Declarations ==================

        // ============================ Attributes ========================
        /** */

        // ============================ Functions =========================
        /** */

        // ============================ Classes ===========================

        
        /** @p file implements a media for tenacitas::log::logger class

         @tparam t_change_file_policy is a policy that defines how a the log
         should continue in another file. It must implement:
         <tt>ptr</tt>, a type of pointer to t_change_file_policy;

         <tt>static ptr create( boost::thread::id )</tt>, which creates a
         pointer to a @p t_change_file_policy object;

         <tt>void on_thread_end(std::ofstream & )</tt>, which is called when the
         thread that is being logged in this file ends;

         <tt>const std::string & file_name( )</tt>, which informs the name of
         the file where the log messages will be written to;

         <tt>bool change_now( std::uint64_t )</tt>, which informs if a new
         file should be created. To help the decision, the current file size
         is passed.


         @tparam t_line_formater is responsible for actually writing the
         log messages to a file. It must implement:

         <tt>void open_file( ofstream & p_file,
                             const std::string & p_file_name ) const</tt>,
         which opens the file @p p_file_name, and associates it to @p p_file


         <tt>void new_line( ofstream & p_file,
                           level p_level,
                           const char * p_file_name,
                           const char * p_function,
                           int64_t p_line )</tt>,
         which starts a new line in the log file. @p p_file is the file where
         a new log line should be recorded; @p p_level is the level of the
         log message; @p p_file_name is the name of the file where the log
         message is being generated; @p p_function is the function where the log
         message is being generated; @p p_line is the line number in
         @p p_file_name where the log message is being generated

         <tt>template <typename t_data>
             void write ( ofstream & p_file, const t_data & p_data )</tt>,
         which writes @p p_data to @p p_file
         <endcode>

         <b>A word to the programmer user</b>
          You, dear programer, won't need to worry about this class, as it will
          be used directly by tenacitas::log::logger class.
         */
        template <typename t_change_file_policy,
                  typename t_line_formater>
        class file {
        public:
            //    file - Public Internal Types

            /** Type for a pointer to @p file */
            typedef std::shared_ptr<file> ptr;

            /** Type for the change file policy*/
            typedef t_change_file_policy change_file_policy;

            //    file - Public Constructors

            /** Destructor
             It is time to notify the change file policy, and the line formater,
             that the thread, which is logging in this file, ended 
             */
            ~file( ) {
            }

            /** Creates a pointer to @p file

              @param [in] p_th_id is the identifier of the thread for which log
              messages will be loged in this file
            */
            static ptr create( boost::thread::id p_th_id ) {
                ptr l_printer( new file( p_th_id ) );
                return l_printer;
            }
            
            /** not allowed */
            file( const file & ) = delete;

            /** not allowed */
            file( file && p_r ) = delete;

            //    file - Public Processors

            /** Writes data to the file
             Writes any data type that to the file, according to the
             @p t_line_formater class

             @param [in] p_data is the data to be written to the file
            */
            template <typename t_data>
            void write ( const t_data & p_data ) {
                m_line_formater->write( m_file, p_data );

                uint64_t l_delta = static_cast<uint64_t>( m_file.tellp( ) ) - 
                                                          m_file_size;
                
                m_file_size += l_delta;
            }

            /** Starts a new line in the file
             This method is called by the tenacitas::log::logger object where
             this @p file object is defined

             @param [in] p_file is the file where a new log line should be
             recorded;
             @param [in] p_level is the level of the log message;
             @param [in] p_file_name is the name of the file where the log
             message is being generated;
             @param [in] p_function is the function where the log message is
             being generated;
             @param [in] p_line is the line number in @p p_file_name where the
             log message is being generated
            */
            void new_line( level p_level, const char * p_file,
                           const char * p_function, int64_t p_line ) {
                if ( m_change_file_policy->change_now( m_file_size ) ) {
                    open_file( );
                }
                
                m_line_formater->new_line( m_file, p_level, p_file,
                                           p_function, p_line );

                uint64_t l_delta = static_cast<uint64_t>( m_file.tellp( ) ) -
                                                          m_file_size;
                
                m_file_size += l_delta;
            }

            /** */
            void on_thread_end( ) {
                m_line_formater->on_thread_end( m_file );
                m_change_file_policy->on_thread_end( m_file );
            }
            
            
            //    file - Public Operators
            
            /** not-allowed */
            file & operator = ( const file & ) = delete;

            /** not-allowed */
            file & operator = ( file && p_r ) = delete;

        private:

            //    file - Private Internal Types

            /** Type of pointer to the @p change_file_policy */
            typedef typename change_file_policy::ptr change_file_policy_ptr;

            /** Type for the line formater */
            typedef t_line_formater line_formater;

            /** Type of pointer to the line formater */
            typedef typename line_formater::ptr line_formater_ptr;

            //    file - Private Constructors

            /** Constructor

              @param [in] p_thread_id is the identifier of the thread for which
              log messages will be loged in this file
            */
            file( boost::thread::id p_thread_id )
                : m_thread_id( p_thread_id ),
                  m_file( ),
                  m_change_file_policy(
                      change_file_policy::create( p_thread_id ) ),
                  m_line_formater( line_formater::create( ) ),
                  m_file_size ( 0 ) {

                open_file( );
            }
            
            //    file - Private Processors

            /** Opens the @p ostream object where the log messages will
                be written to
            */
            void open_file( ) {
                const char * l_file_name =
                        m_change_file_policy->file_name( ).c_str( );

                m_line_formater->open_file( m_file, l_file_name );

                m_file_size = m_file.tellp( );
            }
            
            //    file - Private Attributes

            /** identifier of the thread for which log messages will be loged
             in this file */
            boost::thread::id m_thread_id;

            /** stream where the log messages will be written to*/
            ofstream m_file;

            /** change file policy defined for this @p file */
            change_file_policy_ptr m_change_file_policy;

            /** line formater defined for this policy */
            line_formater_ptr m_line_formater;

            /** current file size */
            uint64_t m_file_size;
        };

        // =========================== Typedefs ===========================

    }
}

// =============================================================================
//                     I M P L E M E N T A T I O N
// =============================================================================

using namespace tenacitas::log;



#endif



// #ifndef __tenacitas_log__file__h__
// #define __tenacitas_log__file__h__


// // =============================================================================
// /** @file In this file are implemented the classes: tenacitas::log::file,
//  tenacitas::log::default_change_file_policy, tenacitas::log::default_text_line
//  and the typedef tenacitas::log::default_text_file */

// // =============================================================================
// /** todo
//     @todo implement class @p default_binary_line
// */

// // =============================================================================
// //                                 C++ Headers
// #include <string>
// #include <cstdint>
// #include <sstream>
// #include <fstream>
// #include <memory>
// #include <chrono>
// #include <cstdio>
// #include <utility>
// #include <type_traits>

// // =============================================================================
// //                                 3rds Headers
// #include <boost/thread/thread.hpp>

// // =============================================================================
// //                                 Our Headers
// #include "level.h"

// // =============================================================================
// //                                 Namespaces
// using namespace std;


// // =============================================================================
// //                                 Macro Contants
// /** */


// // =============================================================================
// //                                 Macro Commands
// /** */


// /** namespace of the organization */
// namespace tenacitas {
//     namespace log {

//         // =========================== Typedefs ===========================
//         /** */

//         // ============================ Pre-Declarations ==================

//         // ============================ Attributes ========================
//         /** */

//         // ============================ Functions =========================
//         /** */

//         // ============================ Classes ===========================

        
//         /** @p file implements a media for tenacitas::log::logger class

//          @tparam t_change_file_policy is a policy that defines how a the log
//          should continue in another file. It must implement:
//          <tt>ptr</tt>, a type of pointer to t_change_file_policy;

//          <tt>static ptr create( boost::thread::id )</tt>, which creates a
//          pointer to a @p t_change_file_policy object;

//          <tt>void on_thread_end(std::ofstream & )</tt>, which is called when the
//          thread that is being logged in this file ends;

//          <tt>const std::string & file_name( )</tt>, which informs the name of
//          the file where the log messages will be written to;

//          <tt>bool change_now( std::uint64_t )</tt>, which informs if a new
//          file should be created. To help the decision, the current file size
//          is passed.


//          @tparam t_line_formater is responsible for actually writing the
//          log messages to a file. It must implement:

//          <tt>void open_file( ofstream & p_file,
//                              const std::string & p_file_name ) const</tt>,
//          which opens the file @p p_file_name, and associates it to @p p_file


//          <tt>void new_line( ofstream & p_file,
//                            level p_level,
//                            const char * p_file_name,
//                            const char * p_function,
//                            int64_t p_line )</tt>,
//          which starts a new line in the log file. @p p_file is the file where
//          a new log line should be recorded; @p p_level is the level of the
//          log message; @p p_file_name is the name of the file where the log
//          message is being generated; @p p_function is the function where the log
//          message is being generated; @p p_line is the line number in
//          @p p_file_name where the log message is being generated

//          <tt>template <typename t_data>
//              void write ( ofstream & p_file, const t_data & p_data )</tt>,
//          which writes @p p_data to @p p_file
//          <endcode>

//          <b>A word to the programmer user</b>
//           You, dear programer, won't need to worry about this class, as it will
//           be used directly by tenacitas::log::logger class.
//          */
//         template <typename t_change_file_policy,
//                   typename t_line_formater>
//         class file {
//         public:
//             //    file - Public Internal Types

//             /** Type for a pointer to @p file */
//             typedef std::shared_ptr<file> ptr;

//             /** Type for the change file policy*/
//             typedef t_change_file_policy change_file_policy;

//             //    file - Public Constructors

//             /** Destructor
//              It is time to notify the change file policy, and the line formater,
//              that the thread, which is logging in this file, ended 
//              */
//             ~file( ) {
//             }

//             /** Creates a pointer to @p file

//               @param [in] p_th_id is the identifier of the thread for which log
//               messages will be loged in this file
//             */
//             static ptr create( boost::thread::id p_th_id ) {
//                 ptr l_printer( new file( p_th_id ) );
//                 return l_printer;
//             }
            
//             /** not allowed */
//             file( const file & ) = delete;

//             /** not allowed */
//             file( file && p_r ) = delete;

//             //    file - Public Processors

//             /** */
//             void on_thread_end( ) {
//                 std::cout << "file::on_thread_end" << std::endl;
                
//                 m_line_formater->on_thread_end( m_file );

//                 m_change_file_policy->on_thread_end( m_file );
//             }
            

//             /** Writes data to the file
//              Writes any data type that to the file, according to the
//              @p t_line_formater class

//              @param [in] p_data is the data to be written to the file
//             */
//             template <typename t_data>
//             void write ( const t_data & p_data ) {
//                 m_line_formater->write( m_file, p_data );

//                 uint64_t l_delta = static_cast<uint64_t>( m_file.tellp( ) ) - 
//                                                           m_file_size;
                
//                 m_file_size += l_delta;
//             }

//             /** Starts a new line in the file
//              This method is called by the tenacitas::log::logger object where
//              this @p file object is defined

//              @param [in] p_file is the file where a new log line should be
//              recorded;
//              @param [in] p_level is the level of the log message;
//              @param [in] p_file_name is the name of the file where the log
//              message is being generated;
//              @param [in] p_function is the function where the log message is
//              being generated;
//              @param [in] p_line is the line number in @p p_file_name where the
//              log message is being generated
//             */
//             void new_line( level p_level, const char * p_file,
//                            const char * p_function, int64_t p_line ) {
//                 if ( m_change_file_policy->change_now( m_file_size ) ) {
//                     open_file( );
//                 }
                
//                 m_line_formater->new_line( m_file, p_level, p_file,
//                                            p_function, p_line );

//                 uint64_t l_delta = static_cast<uint64_t>( m_file.tellp( ) ) -
//                                                           m_file_size;
                
//                 m_file_size += l_delta;
//             }
            
//             //    file - Public Operators
            
//             /** not-allowed */
//             file & operator = ( const file & ) = delete;

//             /** not-allowed */
//             file & operator = ( file && p_r ) = delete;

//         private:

//             //    file - Private Internal Types

//             /** Type of pointer to the @p change_file_policy */
//             typedef typename change_file_policy::ptr change_file_policy_ptr;

//             /** Type for the line formater */
//             typedef t_line_formater line_formater;

//             /** Type of pointer to the line formater */
//             typedef typename line_formater::ptr line_formater_ptr;

//             //    file - Private Constructors

//             /** Constructor

//               @param [in] p_thread_id is the identifier of the thread for which
//               log messages will be loged in this file
//             */
//             file( boost::thread::id p_thread_id )
//                 : m_thread_id( p_thread_id ),
//                   m_file( ),
//                   m_change_file_policy(
//                       change_file_policy::create( p_thread_id ) ),
//                   m_line_formater( line_formater::create( ) ),
//                   m_file_size ( 0 ) {

//                 open_file( );
//             }
            
//             //    file - Private Processors

//             /** Opens the @p ostream object where the log messages will
//                 be written to
//             */
//             void open_file( ) {
//                 const char * l_file_name =
//                         m_change_file_policy->file_name( ).c_str( );

//                 m_line_formater->open_file( m_file, l_file_name );

//                 m_file_size = m_file.tellp( );
//             }
            
//             //    file - Private Attributes

//             /** identifier of the thread for which log messages will be loged
//              in this file */
//             boost::thread::id m_thread_id;

//             /** stream where the log messages will be written to*/
//             ofstream m_file;

//             /** change file policy defined for this @p file */
//             change_file_policy_ptr m_change_file_policy;

//             /** line formater defined for this policy */
//             line_formater_ptr m_line_formater;

//             /** current file size */
//             uint64_t m_file_size;
//         };

//         // =========================== Typedefs ===========================

//     }
// }

// // =============================================================================
// //                     I M P L E M E N T A T I O N
// // =============================================================================

// using namespace tenacitas::log;



// #endif
